#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/types.h>

#ifdef WIN32
#include <sys/unistd.h>
#include <winsock2.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#endif

#include <ociextp.h>

#define BUFFER_TOO_SMALL	-2
#define END_OF_INPUT		0
#define NETWORK_ERROR		-1    
#define TRANSFER_TIMEOUT	-3

#ifndef WIN32
#define min(a,b) ((a) <= (b) ? (a) : (b))
#define max(a,b) ((a) >= (b) ? (a) : (b))	
#endif

#ifdef WIN32
_declspec (dllexport)
#endif
int open_connection(char *hostname, int portno)
{
	int sd = socket(AF_INET, SOCK_STREAM, 0);
	struct sockaddr_in sin;
	struct hostent *host = (struct hostent *)gethostbyname(hostname);
	
	memset((char *) &sin, 0, sizeof(sin));
	memcpy((char *)&sin.sin_addr.s_addr, (char *)host->h_addr, host->h_length);
    sin.sin_family = AF_INET;
	sin.sin_port = htons(portno);
	
	if (connect(sd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
	{
		/*network error*/
		return NETWORK_ERROR;
	}
	return sd;	
}

#ifdef WIN32
_declspec (dllexport)
#endif
int available(	int sd, 
				int timeout, 
				short timeout_i, /*INDICATOR*/
				int timeout_usec, 
				short timeout_usec_i /*INDICATOR*/)
{
	struct timeval tv;
	fd_set readfds;
	
	FD_ZERO(&readfds);
	FD_SET(sd, &readfds);
    
	if ( (timeout_i == OCI_IND_NULL) && (timeout_usec_i == OCI_IND_NULL) ) /*wait forever*/
		select(sd+1, &readfds, NULL, NULL, NULL);
	else
	{
		if (timeout_i == OCI_IND_NOTNULL)
			tv.tv_sec = timeout;
		else
			tv.tv_sec = 0;
		
		if (timeout_usec_i == OCI_IND_NOTNULL)
			tv.tv_usec = timeout_usec;
		else
			tv.tv_usec = 0;

		select(sd+1, &readfds, NULL, NULL, &tv);
	}
	
	if (FD_ISSET(sd, &readfds))
		return 1;
	else
 		return 0;
}

#ifdef WIN32
_declspec (dllexport)
#endif
int read_raw(	int sd, 
				unsigned char * buf, 
				short *	buf_i,		/* INDICATOR */
				int *	buf_ml,		/* MAXLEN */ 
				int *	buf_l,		/* LENGTH */
				int		len )
{
	int total_nobr = 0, nobr = 0;
	unsigned char *recv_buf = NULL;
	
	if (*buf_ml < len)
	{
		/*buffer too small*/
		*buf_i = OCI_IND_NULL;
		*buf_l = 0;
		return BUFFER_TOO_SMALL;
	} 

	recv_buf = (unsigned char *) malloc(*buf_ml);

	while (total_nobr < len)
	{
		nobr = recv(sd, recv_buf+total_nobr, 1, 0);
		if (nobr == 1) 
		{
			total_nobr++;
		}	
		else if (nobr == -1)
		{
			/*network error*/
			*buf_i = OCI_IND_NULL;
			*buf_l = 0;			
			free(recv_buf);
			return NETWORK_ERROR;
		}
		else if (nobr == 0)
		{
			/*encountered end of input*/
			break;
		}		
	}
	
	if (total_nobr == 0)
	{
		/*end of input, read nothing*/
		*buf_i = OCI_IND_NULL;
		*buf_l = 0;
		free(recv_buf);
		return END_OF_INPUT;
	}
	else
	{
		memcpy(buf, recv_buf, total_nobr);
		*buf_i = OCI_IND_NOTNULL;
		*buf_l = total_nobr;
		free(recv_buf);
		return total_nobr;
	}
}

#ifdef WIN32
_declspec (dllexport)
#endif
int write_raw(	int sd, 
				unsigned char* buf, 
				short	buf_i, /* INDICATOR */
				int		buf_l, /* LENGTH */
				int		len,
				short	len_i  /* INDICATOR */ )
{
	int total_nobw=0, nobw=0;
	int wlen;
	
	if (buf_i == OCI_IND_NULL)
	{
		return 0;
	}
	
	wlen = (len_i == OCI_IND_NULL) ? buf_l : min(buf_l,len);
	
	while (total_nobw < wlen)
	{
		nobw = send(sd, buf+total_nobw, wlen-total_nobw, 0);
		if (nobw == -1) /*network error*/
		{
			return NETWORK_ERROR;
		}
		else
		{
			total_nobw += nobw;	
		}
	}
	
	return total_nobw;	
}

#ifdef WIN32
_declspec (dllexport)
#endif
int close_connection(int sd)
{	
	#ifdef WIN32
	return closesocket(sd);
	#else
    return close(sd);
    #endif
}


